الروتين الفرعي


تمنح البيرل استخدام دالة خاصة بك تسمى بالروتين الفرعي subroutine ، في بعض اللغات تعرف بالدالة function أو إجراءات procedures .تساعدنا لتسريع البرنامج فمثلاً اذا كانت الرغبة استخدام مجموعة من التعليمات على مجموعة مختلفة من البيانات في برنامج فرعي اسمه الدالة يمكن استدعاؤه كلما دعت الحاجة الى ذلك ، حيث تملك الدالة اسماً يشير إليه ويستدعى من خلال هذا الاسم بدلاً من كتابة تلك التعليمات في كل مرة ،فمثلاً اذا لديك برنامج لحساب مساحة الدئرة ومساحة المستطيل والمثلث فانك سوف تنشئ ثلاث دوال وكل منها تقوم بوظيفة لإجراء العملية المناسبة وعند الرغبة في حساب مساحة الدائرة مثلاً فأنك سوف تستدعى أسم الدالة وكلما اردت حساب هذة المساحة ستقوم باستدعها مره اخرى ، ويمكن وضع الدالة في اي مكان في برنامجك لكن الافضل وضع جميع الدوال في بداية الكود أو وضعها في النهاية حيث لاتهتم البيرل في حالة إعلنت الدالة قبل او بعد استعمالها وليس من الضروري ان نسبقها بالاعلان لأن البيرل تقوم بالبحث عن أسم الدالة المناسبة عند الإستدعى في كل البرنامج .

لتعريف دالة نستخدم الكلمة المحجوزة sub ، ،على سبيل المثال لتعريف الدالة ثم استدعاءها من أي مكان في البرنامج :

sub mysubroutine 
{
        print "Not a very interesting routine\n";
        print "This does the same thing every time\n";
}

print "main program\n";

# الاشكال المختلفة للاستدعاء
&mysubroutine; 
mysubroutine;
mysubroutine();

في أغلب لغات البرمجة يبدأ التنفيذ من الرئيسية وليس من الشرط في البيرل التقيد بذلك ، البيرل عندما تجد الكلمة المحجوزة sub يتؤقف التنفيذ مباشرتاً عند ذلك الإجراء وينقل التنفيذ خارج بلوك للتعليمة التي تليها ، بداية في المثال السابق تم تعريف اسم الدالة التي تسمى mysubroutine ، نرى ان البرنامج لم ينفذ الدالة الابعد استداعاءها من خلال الاستدعاءات الثالثة المختلفة . ، لكن الملاحظ هنا استعمال استدعى الدالة عن طريق الرمز & فهذا نمط قديم وفي النسخ الحديثة للبيرل ( Perl 5 وما فوق ) هو خياري ، وظيفة العلامة & تقوم بتجاهل اي تعريفات سابقة او اعلان لأغراض الصيغة ولها تأثير حيث انها تتجاهل prototype (كماسنرها لاحقاً ) للدالة اذا كانت معرفة .

الدالة مع الوسيط

فيما سبق الدالة لاتمر فيها اي وسيط، ولكي نستطيع امرار قيم الى الدالة يتم وضع الوسطاء في علامات الحصر التي هي شبية بالـ list constructor[] ، فاذا كانت لدى الدالة وسيط او اكثر فإن الوسيط او الوسطاء تمر كانه مصفوفة من خلال المصفوفة الخاص @_ ، وهي ايضاً كأي مصفوفة عادية يمكن ان نصل اليها من خلال $_[] بواسطة الفهرس المحدد

sub areaOfCircle {
    $radius = $_[0];  #  اول وسيط للدالة 
    return(3.1415 * ($radius ** 2)); 
}
$area = areaOfCircle(5);
print("$area\n");

الكلمة المحجوزة return تقوم بأعادة القيمة الناتجة الى البرنامج الرئيسي ويخصص الى المتغير area ،وبامكان استعمال shift لزالة اول عنصر من المصفوفة الخاصة @_

sub areaOfCircle {
    $radius = shift;  #  اول وسيط للدالة 
    return(3.1415 * ($radius ** 2)); 
}
$area = areaOfCircle(5);
print("$area\n");

المصفوفة الخاصة @_ بامكان الوصول اليها بفهارس كالــ $_[0] $_[1] $_[2] ...... وعندما لاياخذ المتغير الخاص $_ شيء يستعمل بدون خوف من التعارض ،فعلى سبيل المثال عند دفع اكثر من وسيط

sub firstSub {
    $element=shift @_;
    print("@_ \n shift element: $element\n");

}

firstSub(1, 2, 3, 4, 5, 6);
firstSub(1..3);  # الوسطاءفي قائمة 
firstSub("A".."Z");


اعادة قيمة بواسطة المرجع

عند استدعاء وسيط كامتغير فأن اي تعديل على المتغير الخاص علية سيرجع الى ذلك المتغير فعلى سبيل المثال

sub firstSub{
    $_[0] = "A";
    $_[1] = "B";
}

@array = (0..5);
print("Before function call, array = @array\n");
firstSub(@array);
print("After function call, array =  @array\n");


Output

Before function call, array =  0 1 2 3 4 5

After function call, array =   A B 2 3 4 5


اذا اردت تغير بدون إرجاع

sub firstSub{
   ($firstVar, $secondVar) = @_ ;
   $firstVar = "A";  
   $secondVar = "B";
}

لكن هناك مشكلة شاهد المثال التالي

sub firstSub{

    ($firstVar, $secondVar) = @_ ;

    $firstVar = "A";
    $secondVar = "B";
}

$firstVar = 10;
@array    = (0..5);

print("Before function call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray    = @array\n");

firstSub(@array);

print("After function call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray    = @array\n");

Output

Before function call

        firstVar = 10
        array    = 0 1 2 3 4 5
After function call
        firstVar = A
        array    = 0 1 2 3 4 5


ولحل ذلك نستعمل دالة my او باستعمال الدالة local افضل لتصريح عن متغيرات محلية كما سنراه لاحقاًُ لتصريح عن متغيرات الحزم

sub firstSub{

    local($firstVar, $secondVar) = @_ ;

    $firstVar = "A";
    $secondVar = "B";
}

$firstVar = 10;
@array    = (0..5);

print("Before function call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray    = @array\n");

firstSub(@array);

print("After function call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray    = @array\n");

Output

Before function call

        firstVar = 10
        array    = 0 1 2 3 4 5
After function call
        firstVar = 10
        array    = 0 1 2 3 4 5


القيمة الافتراضية

المؤثر المنطقي or "||" فهي تعيد على الاقل واحد صحيح فعلى سبيل المثال المتغير يأخذ القيمة الصحيحة اسفل وهي 3 بغض النظر عن القيم الصحية الاخرى ، بواسطة $a=0||5; الصفر قيمة اخاطئة فالمتغير ياخذ القيمة الصحيحة وهي 5

$a=3||5;
print $a,"\n";

لذالك اي شيء تعطى الى المصفوفة الخاصة @_ تكون قيمة صحيحة true وغير ذلك فهي false

#!/usr/bin/perl
# defaults.pl
use warnings;
use strict;
sub log_warning {
my $message = shift || "Something's wrong";
my $time = shift || localtime; # Default to now.
print "[$time] $message\n";
}
log_warning("Klingons on the starboard bow", "Stardate 60030.2");
log_warning("please delete more people");
log_warning();

Output

[Stardate 60030.2] Klingons on the starboard bow
[Thu Sep  4 20:08:41 2008] please delete more people
[Thu Sep  4 20:08:41 2008] Something's wrong




مرور الهاشات والقوائم

فاذا كانت الرغبة امرار مصفوفة او هاش الى الدالة من خلال الوسيط نحتاج للإتخاذ خطوات اضافية فعلى سبيل المثال

my $message = "Testing";
my @count = (1, 2, 3);
testing ($message, @count);   # calls 'testing' — see below
# بنفس
testing ("Testing", 1, 2, 3);

sub testing {
my ($message, @count) = @_;
...
}

ونفس العمل مع الهاش التي تظهر بقدر اكثر للمصفوفة الخاصة @_ واستعادة الهاش فيما بعد

sub testing {
my ($message, %count) = @_;
print "@_";
}
testing ("Magpies", 1 => "for sorrow", 2 => "for joy", 3 => "for health",
4 => "for wealth", 5 => "for sickness", 6 => "for death");

البيرل تسمح بامرار الرمز => التي مماثلة لرمز الفاصلة، على اي حال هذة الاعمال الوحيدة التي تستطيع فعلها بسبب ان المتغير سوف ياخذ اول وسيط والباقية تخصص الى المصفوفة او الهاش واذا تم عبور القائمة اولاً وبعد ذلك المتغير فان كل الوسطاء تخصص الى القائمة والمتغير يترك غير معرف

sub testing {
my (@count, $message) = @_;   # ERROR
print "@_";
}
testing(1, 2, 3, "Testing");
# results in @count = (1, 2, 3, "Testing") and $message = undef

واذا عرفت كما في الاسفل بعبور اكثر من قائمة المفسر يعطي تحذير اذا تاخذ المصفوفة الاولة كل القوائم والثانية تترك بدون تعريف

sub testing {
my (@messages, @count) = @_; # wrong!
print "@_";
}
my @msgs = ("Testing", "Testing");
my @count = (1, 2, 3);
testing(@msgs, @count);
# results in @messages = ("Testing", "Testing", "Testing", 1, 2, 3)
# and @count = ();

الطريقة الصحيحة لعبور اكثر من قائمة او هاشات بواسطة المراجع ليبقي سليماً في الشكل الذي جهزناه اصلاً سوف تناقش فيما بعد

testing (["Testing", "Testing"], [1, 2, 3]); # with two lists
testing (\@messages, \@count);   # with two array variables
testing ($aref1, $aref2);   # with two list references


الدوال الخاصة BEGIN & END

فبلوك BEGIN ينفذ خلال التجميع وقبل الدخول الى البرنامج الرئيسي ، لذا محتوي هذا البلوك يجمع وينفذ حتي قبل ان يجمع الكود الرئيسي للبرنامج ،ويستعمل عادة لعطى المتغيرات بقيم ابتدائية والتخصيصات اللزمة ، اما بلوك END ينفذ بعد الخروج من البرنامج ، ويستعمل عادتاً لعمل تنظيف للمتغيرات المحجوزة اوالخروج من قاعدة البيانات ، او حذف الملفات المؤقتة وسيتم ايضاً مناقشها فيما بعد

على سبيل المثال

#!/usr/bin/perl
# begend.pl
use warnings;
use strict;
END {
print "Exiting... \n";
}
print "Running! \n";
fun();
sub fun {
print "Inside fun \n";
}
BEGIN {
print "Compiling... \n";
# can't call 'fun' - not compiled yet
# fun();
}

Output

Compiling...
Running!
Inside fun
Exiting...


لمعرفة معلومات عن دالة داخلية من سطر الاوامر man perlfunc او perldoc perlfunc ، لمعرفة معلومات عن دالة perldoc -f print


Prototypes